package ms.jasim.framework;

import java.util.HashMap;
import java.util.Map;

import ms.jasim.framework.JEventType.EventTimeType;
import ms.jasim.framework.SolutionSimulator.SimulatorArg;

import com.l2fprod.common.propertysheet.DefaultProperty;
import com.l2fprod.common.propertysheet.Property;

public class JEventInstance {

	public final JEventType Type;
	private Map<String, Object> paramValue = new HashMap<String, Object>();

	private String id;

	static int Counter = 0;

	public static class JEventInstanceProperty extends DefaultProperty {

		private static final long serialVersionUID = 1L;
		JEventType.Parameter param;

		public JEventInstanceProperty(JEventType.Parameter param) {
			this.param = param;

			setName(param.getName());
			setDisplayName(param.getText());
			setShortDescription(param.getDescription());
			setType(param.getType().getBaseType());
			setCategory("General");
		}

		@Override
		public void readFromObject(Object object) {
			if (object instanceof JEventInstance) {
				try {
					JEventInstance ins = (JEventInstance) object;
					Object value = ins.getParamValue(param.getName());
					initializeValue(value); // avoid updating parent or
					// firing property change
					if (value != null) {
						for (Property subProperty : getSubProperties())
							subProperty.readFromObject(value);
					}
				} catch (Exception e) {
					throw new RuntimeException(e);
				}

			}
		}

		@Override
		public void writeToObject(Object object) {
			if (object instanceof JEventInstance) {
				try {
					JEventInstance ins = (JEventInstance) object;
					ins.setParamValue(param.getName(), getValue());
				} catch (Exception e) {
					throw new RuntimeException(e);
				}

			}
		}
	}

	public static class SubProperty extends DefaultProperty {
		private static final long serialVersionUID = 1L;

		public SubProperty(String propName, String displayText, Class type) {
			// this.host = host;
			this.setName(propName);
			this.setDisplayName(displayText);
			this.setCategory("General");
			this.setType(type);
		}
	}

	public JEventInstance(JEventType type) {
		Type = type;
		id = "E" + ++Counter;
		setParamValue(JEventType.TIME, new JEventType.EventTime());
		setParamValue(JEventType.REPLAN_OPT, new JEventType.EventReplanOption());
	}

	@SuppressWarnings("unchecked")
	public <T> T getParamValue(String paramName) {
		JEventType.Parameter param = Type.Parameters.get(paramName);
		return (T) (paramValue.containsKey(paramName) ? paramValue.get(paramName) : param != null ? param.getDefaultValue() : null);
	}

	public <T> void setParamValue(String paramName, T value) {
		paramValue.put(paramName, value);
	}

	public int getTime() {
		JEventType.EventTime time = getParamValue(JEventType.TIME);
		return time != null ? time.getAbsTime() : 0;
	}

	public void setTime(int value) {
		JEventType.EventTime time = getParamValue(JEventType.TIME);
		if (time != null)
			time.setAbsTime(value);
	}

	public void setMaxReplanSolution(int value) {
		JEventType.EventReplanOption opt = getParamValue(JEventType.REPLAN_OPT);
		if (opt != null)
			opt.setNumberOfSolutions(value);
	}

	public void setMaxProcessTime(int value) {
		JEventType.EventReplanOption opt = getParamValue(JEventType.REPLAN_OPT);
		if (opt != null)
			opt.setMaxProcessTime(value);
	}

	public String getID() {
		return id;
	}

	@Override
	public String toString() {
		return String.format("[%s] %s", id, Type.getName());
	}

	public boolean canTriggerNow(SimulatorArg arg) {
		JEventType.EventTime time = getParamValue(JEventType.TIME);
		if (time.getType() == EventTimeType.ABSOLUTE)
			return time.getAbsTime() == arg.getTime();
		else {
			for (SolutionSimulator.Agent ag : arg.getSimulator().Agents)
				if (time.getActor() == null || ag.getName().equalsIgnoreCase(time.getActor().getName()))
					for (SolutionSimulator.SimulatedAction act : ag.ActionQueue) {
						if (act.getGoal() != null
								&& act.getAssociateAction().getFunctor().toString().equalsIgnoreCase(time.getAction().toString())
								&& act.getGoal().getName().equalsIgnoreCase(time.getGoal().getName()) && act.isExecuting()
								&& act.getStart() + time.getAbsTime() == arg.getTime())
							return true;
					}
		}
		return false;
	}

}
